import java.util.ArrayList;
import java.util.Iterator;

public class Code {
	
	/*
	 * Class representing codes, i.e. sets of dinucleotids
	 */
	
	// Attributes
	
	private ArrayList<Dinucleotide> code = new ArrayList<Dinucleotide>();
	
	// Constructors
	
	public Code() { }
	
	public Code(Code c)
	{
		this.code = c.get_code();
	}
	
	public Code(Dinucleotide d)
	{
		code.add(d);
	}
	
	public Code(ArrayList<Dinucleotide> list)
	{
		for (Dinucleotide d : list)
		{
			code.add(d);
		}
	}
	
	// Getters / Setters

	public ArrayList<Dinucleotide> get_code()
	{
		return code;
	}
	
	public void add(Dinucleotide d)
	{
		code.add(d);
	}
	
	public void add(Code c)
	{
		ArrayList<Dinucleotide> gc = c.get_code();
		for(Dinucleotide d : gc)
		{
			this.add(d);
		}
	}
	
	public void remove(Dinucleotide d)
	{
		code.remove(d);
	}
	
	// Code length
	public int length()
	{
		return code.size();
	}
	
	// Convert a code into a string
	public String toString()
	{
		String  str   = "";
		boolean first = true;
		
		for (Dinucleotide d : code)
		{
			if (!first)
			{
				str += " ";
			}
			str += d.toString();
			first = false;
		}
		return str;
	}
	
	// Redefinition of the equality between two codes
	public boolean equals(Object o)
	{
		Code c = (Code) o; 
		
		for (Dinucleotide d : c.get_code())
		{
			if(!((this.get_code()).contains(d)))
			{
				return false;
			}
		}
		return true;
	}
	
	// Return the complementary of a code
	public Code complementarity()
	{
		Code c = new Code();
		
		for (Dinucleotide d : code)
		{
			Dinucleotide d2 = d.complementarity();
			c.add(d2);
		}
		return c;
	}	
	
	// Return the permutation of a code
	public Code permutation()
	{
		Code c = new Code();
		
		for (Dinucleotide d : code)
		{
			Dinucleotide d2 = d.permutation();
			c.add(d2);
		}
		return c;
	}	
		
	// Return true if the code contains a 5-necklace
	public boolean hasNecklace5()
	{
		boolean flag = false;
		
		Iterator<Dinucleotide> itr = code.iterator();
		
		while (!flag && itr.hasNext())
		{
			Dinucleotide tmp = itr.next();
			flag = hasNecklace5_rec(tmp, tmp, 1);
		}		
		return flag;
	}
	
	// Function used in hasNecklace5() 
	private boolean hasNecklace5_rec(Dinucleotide f, Dinucleotide d, int l)
	{
		boolean flag = false;
		
		if (l > 4)
		{
			return true;
		}
		else
		{
			Iterator<Dinucleotide> itr = code.iterator();
			while (!flag && itr.hasNext())
			{
				Dinucleotide tmp = itr.next();
				
				if (d.right == tmp.left)
				{
					flag = hasNecklace5_rec(f, tmp, l+1);
				}
			}
		}	
		return flag;
	}
	
	// Return true if the code is maximal
	public boolean is_maximal(ArrayList<Code> set1)
	{		
		boolean flag = true;
		Code    tmp  = new Code(this.get_code());
		Code    current;
		Iterator<Code> itr = set1.iterator();
		
		// On essaye d'ajouter les dinucléotides
		while (flag && itr.hasNext())
		{
			current = itr.next();
			
			if (!tmp.get_code().contains(current.get_code().get(0)))
			{
				tmp.add(current);
				
				// Si il n'y a pas de collier, le nouveau code est circulaire
				// donc le code n'était pas maximal !
				flag = tmp.hasNecklace5();
				
				tmp.remove(current.code);
			}
		}
		
		return flag;
	}

	private void remove(ArrayList<Dinucleotide> current)
	{
		for (Dinucleotide d : current)
		{
			remove(d);
		}		
	}
}